home *** CD-ROM | disk | FTP | other *** search
/ The 640 MEG Shareware Studio 2 / The 640 Meg Shareware Studio CD-ROM Volume II (Data Express)(1993).ISO / pascal / mltsktp.zip / TP4MULTI.DOC < prev    next >
Text File  |  1988-09-22  |  61KB  |  1,808 lines

  1. Dear Programmer,
  2.  
  3. thanks for using my Multi-Tasking Subsystem for Turbo Pascal 4.0.
  4. This product was designed to enable everyone to study programming in a
  5. multi-tasking environment, without having to spend a lot of money to
  6. purchase an operating system like OS/2 or XENIX (and of course the
  7. extension memory necessary to run it).
  8.  
  9. The Turbo Pascal Multi-Tasking Subsystem is a very inexpensive and
  10. yet powerful tool that adds multi-tasking capabilities to your
  11. Turbo Pascal 4.0 programs.
  12. You will be able to study the programming of parallel processes and
  13. problems of interprocess communication using the programming
  14. environment you are accustomed to.
  15.  
  16. Although the Multi-Tasking Subsystem initially was designed as a means
  17. of experimenting, it became very soon a powerful and easy to use
  18. product, which I hope you will like as much as I do.
  19.  
  20. At first, I would like to outline the capabilities of the basic system:
  21.  
  22. - up to 50 (increased on demand) independently executing
  23.   tasks in a single program
  24. - 3 priority levels
  25. - code sharing
  26. - preemptive scheduler, using a dynamic scheduling algorithm
  27. - you may use DOS-functions in your tasks safely. A task is never
  28.   interrupted within a DOS-function or a critical interrupt
  29. - size of timeslice and range of dynamic CPU-allocation programmable
  30. - message passing
  31. - semaphores
  32. - programmable timers
  33. - primitive event processing (up to 10 simultaneously active event
  34.   requests; increased on demand)
  35. - executes on any PC, XT, AT, PS/2 and full compatible running DOS 2.x
  36.   or 3.x
  37. - source code available to registered users
  38.  
  39.  
  40. Registered users will get an additional UNIT for FREE(!!) which is
  41. based upon the Multi-Tasking Subsystem and provides you with:
  42.  
  43. - extended keycodes (Chacter, Scan-Code, Shift-Statusword)
  44. - manipulation of the keyboard buffer (clear, add keycodes at the
  45.   end or in front of the actual buffer contents)
  46. - keyboard lock and unlock
  47. - execute a DOS-program as subtask
  48. - all you need to have tasks wait for a special hot-key that awakes
  49.   them
  50. - all you need to have tasks pop up over the DOS-program and suspend
  51.   the DOS-task while they are executing (another way of writing memory
  52.   resident programs)
  53.  
  54.  
  55. Well, no product is perfect!  If you would like to have a function the
  56. Multi-Tasking Subsystem does not include at present, contact me and
  57. perhaps you will find it in the next version. IF I decide to
  58. realize YOUR idea in a later version of the subsystem, you will
  59. receive a FREE-upgrade!  I hope you will understand that I reserve
  60. the right to decide which functions will be added.
  61.  
  62. In this connection, I would like to thank L. David Baldwin and
  63. TurboPower Software for their fabulous source-level debugger TDebug
  64. PLUS 4.01, that helped me very much during the development of my
  65. Multi-Tasking Subsystem. By the way, most programs written with the
  66. Multi-Tasking Subsystem may be debugged with TDebug PLUS.
  67.  
  68.  
  69. Let me stop here!  Please take the time to read carefully through the
  70. documentation before you "dive" into the world of multi-tasking.
  71.  
  72.  
  73. Best regards,
  74.  
  75.    Christian Philipps
  76.  
  77.  
  78.  
  79.  
  80.  
  81.  
  82.  
  83.                    Turbo  4.0  Multi-Tasking Subsystem
  84.  
  85.  
  86.                               User's Manual
  87.  
  88.  
  89.  
  90.  
  91.  
  92.  
  93.                            (c) Copyright 1988 by
  94.                          Christian Philipps, Moers
  95.                             all rights reserved
  96.  
  97.                        Version 1.10  / September 1988
  98.  
  99.  
  100.  
  101.  
  102.  
  103.  
  104.  
  105.  
  106.  
  107.  
  108.  
  109.  
  110.  
  111.  
  112.                      Author:   Christian Philipps
  113.                                Hülsdonker Str. 139a
  114.                                4130 Moers 1
  115.                                West-Germany
  116.  
  117.                                Fido-Net: PD-Shuttle Bayreuth
  118.                                          Tel.: 0921/67170
  119.                                          Node: 2:509/2
  120.                                          User: Christian Philipps
  121.  
  122.  
  123.  
  124.  
  125. Trademarks Mentioned
  126.  
  127. TurboPower Software is trademark of TurboPower Software, Scotts Valley
  128.  
  129. Turbo Professional is trademark of Sunny Hill Software, used under
  130. license to TurboPower Software
  131.  
  132. Turbo Pascal is registered trademark of Borland International
  133.  
  134. MS-DOS, OS/2 and MASM are trademarks of Microsoft Corporation
  135.  
  136. A86 is trademark of Eric Isaacson Software, Bloomington
  137.  
  138. UNIX is trademark of AT & T
  139.  
  140. IBM PC, XT, AT, PS/2 and PC-DOS are trademarks of International
  141. Business Machines Corporation
  142.  
  143.  
  144.  
  145. License Agreement
  146.  
  147. The Turbo Pascal Multi-Tasking Subsystem is marketed through the
  148. ShareWare marketing concept.
  149.  
  150. Therefore I grant you the right to reproduce, distribute and use
  151. copies of this ShareWare version of our Multi-Tasking Subsystem
  152. (including the on disk documentation), on the express condition that
  153. you do not receive any payment, commercial benefit, other
  154. consideration for such reproduction or distribution, or change this
  155. license agreement or the copyright notice.
  156.  
  157. The Multi-Tasking Subsystem, the additional unit MTPOPUP and the
  158. documentation of both are copyright (c) 1988 by Christian Philipps,
  159. Hülsdonker Str. 139a, 4130 Moers 1, West-Germany.
  160.  
  161. You have the right to evaluate this ShareWare version for a period of
  162. 30 days to find out whether it suits your needs. If you decide to
  163. continue using this software, I expect that you become a registered
  164. user by sending DM 50 or $35 (object code license) / DM 100 or $70
  165. (object code + source code license) to
  166.  
  167.               Christian Philipps
  168.               Hülsdonker Str. 139a
  169.  
  170.               4130 Moers 1
  171.               West-Germany
  172.  
  173. As soon as I receive your registration, I will send you your
  174. personal copy of the subsystem plus a FREE unit based upon the basic
  175. subsystem (see introductory letter).
  176. Furthermore you will get technical support through mail, e-mail or
  177. phone during 1 year from the day of receipt.
  178.  
  179. Users purchasing an oject code license only may receive the source
  180. code of the latest version later at any point of time by sending
  181. another DM 50 or $35 to the adress above.
  182.  
  183.  
  184.  
  185. Disclaimer
  186.  
  187. I make no warranty of any kind, either express or implied, including
  188. but not limited to implied warranties of merchantability an fitness
  189. for a particular purpose, with respect to this software and the
  190. accompanying documentation.
  191.  
  192. In no event shall I be liable for any damages (including damages for
  193. loss of business profits, business interruption, loss of business
  194. information, or other pecuniary loss) arising out of the use of or
  195. inability to use this software, even if I have been advised of the
  196. possibility of such damages.
  197.  
  198.  
  199.  
  200.  
  201. Getting Started
  202.  
  203. Throughout this documentation I assume that you are familiar with
  204. Turbo Pascal 4.0 in general and with the UNIT concept.
  205. I also assume that you know how to use the advanced features of Turbo
  206. Pascal 4.0, for example type-casting of pointers. If necessary,
  207. please refer to your Turbo Pascal documentation for more information
  208. on any of the compiler's internal functions or the functions contained
  209. in the units Crt, Dos,...
  210.  
  211.  
  212. The Multi-Tasking Subsystem mainly consists of three units:
  213.  
  214. a) TP4MULTI.TPU
  215.  
  216.    This is the basic Multi-Tasking Subsystem. All further units are
  217.    based upon this piece of software.
  218.  
  219. b) MTPOPUP.TPU (registered users only)
  220.  
  221.    MtPopUp is a unit, based upon the basic subsystem and providing
  222.    full control over the system keyboard and supports one DOS-program
  223.    to be run as a separate task. Tasks may be kept waiting for a
  224.    certain key-combination to be pressed and somewhat resident
  225.    programs can be written by executing another COMMAND.COM in the
  226.    foreground. Please note that this unit is available only to
  227.    registered users, who will receive it in return for their
  228.    registration.
  229.  
  230. c) QUEUE.TPU (Public-Domaine)
  231.  
  232.    This unit contains several functions, which help you to create,
  233.    delete and manage forward-chained list-structures. The structure
  234.    of a single list element is defined by your application and may be
  235.    any data structure. Passing data between separate tasks, you will
  236.    shortly find out that list structures, as provided by this unit,
  237.    are convenient for implementing pipe-like data structures.
  238.    Studying the routines contained in the accompanying source code for
  239.    QUEUE, you will learn how to assure the consistency of shared data
  240.    at any time during execution.
  241.  
  242.  
  243. Let's take a look at the way to USE the Multi-Tasking Subsystem: The
  244. usage of this software is quite straightforward, provided you enable
  245. your compiler to find the necessary units (please refer to your
  246. compiler manual).
  247. You will incorporate the Multi-Tasking library in your programs by
  248. simply including TP4MULTI in the USES-statement. TP4MULTI must be the
  249. first unit of the subsystem preceded, however, by the basic units Crt
  250. (or alternatives) and Dos.
  251.  
  252. Example: USES Crt, Dos, Tp4Multi, MtPopUp, Queue;
  253.  
  254. The units TP4MULTI and MTPOPUP use Turbo's CRT-unit, so problems may
  255. arise, when using an alternative CRT-unit (such as the TurboProfes-
  256. sional unit TPCRT) in your program.
  257. For the special case that you own the fantastic TurboProfessional
  258. package of TurboPower Software, your diskette contains files with
  259. the extension .PRO, which I compiled using the TPCRT-unit. You only
  260. need to rename those files to the extension .TPU.
  261.  
  262. If you wish to use another alternative Crt-unit that conflicts with
  263. the standard CRT-unit, you will have to order the source code and
  264. recompile the subsystem.
  265.  
  266. After having set up your USES-statement properly, you will have to
  267. include the compiler switch S-, which prevents the compiler
  268. from generating object code that checks the stack boundaries. This
  269. is VERY important, because each task has its own private stack
  270. that is automatically reserved on the heap at the time of creation.
  271. Without the S- directive, you would get a stack overflow abort
  272. shortly after creating the first subtask.
  273.  
  274. Whenever a task-switch is performed, the Multi-Tasking Subsystem
  275. checks the private stack of a subtask A task which uses up its
  276. stackspace (minus a certain safety margin) is aborted. The subsystem
  277. issues an error message and marks the task as "Crashed". The memory
  278. occupied by its private stack is not freed and may be examined using a
  279. debugger.
  280.  
  281. Now let's have a look at the system startup. When your program comes
  282. to execution the initialisition code in TP4MULTI captures some
  283. interrupt vectors and sets up the task-table. It creates a
  284. null-process with lowest priority (a simple loop) that consumes
  285. CPU-time whenever no other process is currently computable. Finally
  286. the main program is started as task no. 2.
  287.  
  288. The shutdown again is fully automatic. When your program terminates
  289. or a runtime error brings you to a halt, TP4MULTI's exit procedure
  290. gains control, disables multi-tasking and restores the interrupt
  291. vectors previously captured.
  292. Only in rare occasions, your system will completely crash. This
  293. could happen, when one of your tasks runs mad and overwrites
  294. valuable system data.
  295.  
  296. You might start experimenting right now, BUT I recommend studying
  297. the rest of this documentation before. The following sections will
  298. give you a deeper understanding of the concept and the interior of the
  299. Multi-Tasking Subsystem.
  300. Multitasking is too complicated however, to go down to the last.
  301. Those of you, who would like do get a deeper insight into the design
  302. of multi-tasking operating systems should read the book "Operating
  303. Systems, Design and Implementation" by Andrew S. Tanenbaum, Prentice
  304. Hall or scan through the bookshelves of Prentice Hall or Adison
  305. Wesley. The book mentioned above helped me very much in designing the
  306. Multi-Tasking Subsystem and describes a UNIX-like operating system for
  307. personal computers in detail (MINIX + source code available from
  308. Prentice Hall).
  309.  
  310.  
  311. The Basic System (TP4MULTI)
  312.  
  313. 1. Introduction
  314.  
  315. At the moment, more and more programmers become interested in multi-
  316. tasking environments for personal computers. I suppose, anyone who
  317. intends to stay up to date, will have to deal with systems like OS/2
  318. or UNIX / XENIX /...
  319. Unfortunately, those systems are quite expensive and require lots of
  320. extension memory. Only few wealthy private programmers (have you
  321. ever seen one?) will be able to buy such an operating system already
  322. today.
  323.  
  324. At the time I started developing the Multi-Tasking Subsystem, my
  325. situation had been as described above. Now I'm in the position to
  326. provide you with an inexpensive means to study the design and
  327. synchronisation of parallel processes. In addition to that, the
  328. Multi-Tasking Subsystem will help you to write programs more complex
  329. and much more sophisticated, especially in areas like data
  330. communication, where quite a lot of actions are to be taken nearly
  331. simultaneously.
  332.  
  333.  
  334. Concept
  335.  
  336. Like UNIX or OS/2, the Multi-Tasking Subsystem was designed as a
  337. time-sharing system. It was not designed for real-time applications.
  338. A time-sharing system tries to divide the available computing power
  339. among all computable processes evenly. Therefore, a process that has
  340. used up its timeslice, is temporarily suspended and another
  341. computable process gains control (preemptive scheduling). This is
  342. accomplished by having a timer-interrupt-service-routine monitor the
  343. system activities about 18 times a second. This routine initiates a
  344. task-switch whenever the running task has used up its quantum.
  345. Although I could have used the AT-RTC (Real-Time Clock), which in fact
  346. would decrease the minimum possible timeslice, I have decided not to
  347. do so for several reasons:
  348.  
  349. a) The task-switch is the most time consuming action, the kernel has
  350.    to perform. A smaller timeslice, increases the number of possible
  351.    task-switches, but increases the kernel overhead as well.
  352.  
  353. b) The Multi-Tasking Subsystem could not run on PC or XT-computers,
  354.    because those do not have a real-time clock.
  355.  
  356. With a timer-interrupt occuring 18 times a second, the smallest
  357. possible timeslice is about 55,5 milliseconds. During the
  358. initialisation sequence the timslice is set to 110 milliseconds.
  359. If you choose a 55,5 millisecond timeslice the amount of computing
  360. power consumed by the mere task-switching process will be about 1,4
  361. percent (12 MHz AT-compatible).
  362.  
  363.  
  364. Dynamic Scheduling
  365.  
  366. Normally each task is temporarily suspended whenever it has used up
  367. its timeslice. If this happes inside a DOS-function, however, the
  368. kernel may not safely interrupt the currently executing task. It has
  369. to wait, until it returns from the DOS-call.
  370. Provided this task has to read thousands of records from a data file
  371. and write them to another file, the kernel will be inside DOS
  372. again, when the kernel tries to suspend it at the following
  373. timer-tick. Again, a task switch is impossible! This procedure might
  374. happen again and again until the task has finished.
  375. As you see, under certain circumstances, a task may exceed its
  376. quantum at an amount that cannot be foreseen by the system-kernel.
  377.  
  378. On the other hand, there might be a task that periodically, lets say
  379. one time a second, look at a particular value taken from a port to
  380. see, whether actions are to be taken. In this case, it will act upon
  381. the condition encountered, otherwise it will go to sleep and give up
  382. its timeslice. Such a task will use up its timeslice very seldom.
  383. If, hovewer, actions are to be taken, it might be suspended in the
  384. middle of its activities because it has used up its quantum. - Indeed
  385. that's not fair!
  386.  
  387. What can we do to achieve some kind of compensation? OS/2 brought me
  388. to the idea to implement a somewhat dynamic scheduling that proceeds
  389. as follows: Whenever a task gives up its timeslice, the time not used
  390. is placed to this task's credit. If it has to execute longer at a
  391. later point of time, it will not be preempted until it has used up its
  392. current quantum plus its credit. On the other hand, a task that
  393. exceeds its quantum, is not rescheduled until its debit is "used up".
  394.  
  395. This sounds quite good, but there is a catch to it though. If we
  396. would credit every single tick not used, the task mentioned above
  397. could possibly run for hours if it has to take some action after
  398. all.
  399. For this reason, the amount of ticks beeing placed to a tasks
  400. credit/debit, is limited. You may choose this limit freely and it
  401. defaults to +/- 10 timer-ticks that is slightly above half of a
  402. second.
  403.  
  404.  
  405. Priority
  406.  
  407. The Multi-Tasking Subsystem supports three levels of priority:
  408. "Pri_Nice" which is the lowest, "Pri_User" which is standard and
  409. "Pri_Kernel" which is highest possible priority. Your Pascal
  410. main-program is started at the level "Pri_User" during initialization.
  411.  
  412. Let's examine, how the different layers of priority are acted upon:
  413. Inside each layer, the tasks are scheduled in straight round-robin
  414. fashion. Whenever there are computable tasks at a higher level none
  415. of the tasks in a level below that will gain control. That means, the
  416. "idle loop" at level "Pri_Nice" will only be activated, if the
  417. layers "Pri_User" and "Pri_Kernel" are empty.
  418.  
  419. The programmer has to take this concept into account when designing
  420. his application system. The overall system performance depends very
  421. much on the assignment of priorities.
  422.  
  423. By the way: Throughout this documentation I use the terms "task" and
  424. "process" interchangably, although some experts will be picking up
  425. stones to throw at me at this very moment...  I don't feel guilty
  426. doing so, because those experts seem not to have reached an agreement
  427. yet, about how to define these terms.
  428.  
  429. Tasks that spend most of their lives waiting for events (waiting for
  430. incoming characters,...), should be given highest priority.
  431. Tasks that spend their lives busily working all the time, should be
  432. given standard pritority.
  433. By doing so, the busy ones will be interrupted whenever an event, a
  434. higher priorized task was waiting for occurs and will be able to work
  435. the rest of the time (which will be most of the time) undisturbed.
  436. The lowest priority level will mostly be occupied only by the idle
  437. loop.
  438.  
  439.  
  440. Another Catch To It
  441.  
  442. DOS does not know about interrupt controlled disk I/O in the
  443. fashion of multi-tasking operating systems! There is a loop inside
  444. your ROM that waits for the disk-controller to issue a completion
  445. interrupt, but that is of no use for us, because a disk I/O
  446. operation must never be interrupted by a task-switch.
  447. Multi-tasking operating systems normally switch to another task,
  448. while the currently running process has to wait for a disk operation
  449. to complete. I didn't know how to achieve this without rewriting
  450. the low-level disk I/O-routines. So for now, we'll wait...
  451.  
  452. As a consequence, a process that performs lots of disk I/O-operations
  453. and which is running at high priority, will possibly block the whole
  454. system.
  455. In a real multi-tasking operating system however, it is very useful
  456. running I/O-intensive processes at a high priority level for the
  457. reasons stated earlier in the context of mostly waiting tasks.
  458.  
  459.  
  460. Inter Process Communication (IPC)
  461.  
  462. The Multi-Tasking Subsystem contains functions for handling
  463. semaphores and message-passing.
  464. Shared-memory does not require system support, because every task
  465. under DOS has access to any memory location in the whole system.
  466. The programmer will have to use semaphores, however, to avoid race
  467. conditions when accessing commonly used memory areas. The
  468. demonstration program PRO_CON gives examples of how to synchronize
  469. your tasks using semaphores.
  470.  
  471.  
  472. Programmable Timers
  473.  
  474. Programmable timers are a very useful means to take care of timeout-
  475. conditions (for example in communications software).
  476. The Multi-Tasking Subsystem let you define any number (limited by
  477. the available heap) of timers with a resolution of 55,5
  478. milliseconds. If a timer expires, a byte located at the address given
  479. at the time of creation is incremented.
  480. The amount of CPU-time consumed by watching the timers currently
  481. active is negligible and independent of the number of timers defined.
  482.  
  483.  
  484. Event Handling
  485.  
  486. Sometimes it can be useful to have a task waiting for the contents of
  487. a memory location to change. This is what I have chosen to call an
  488. event. Realizing a task that waits for a buffer filled by an
  489. interrupt routine, you can acchieve a very short response time at a
  490. minimum waste of CPU-time by defining an event that reactivates your
  491. task whenever the contents of the buffer's tail-pointer changes.
  492.  
  493.  
  494.  
  495. 3. How Is It Done
  496.  
  497. Most of the subsystem is written in Turbo Pascal 4.0. Only a small
  498. part of the kernel (about 2 KB) is written in assembly language. If
  499. you purchased a source code license, you will find two versions of
  500. the assembly language module on your product diskette. The first one
  501. assembles using Microsofts Macro Assembler V4.0 or higher, the second
  502. one is for programmers using the well known ShareWare assembler A86
  503. V3.19 or higher, written by Eric Isaacson.
  504. Both parts together form a TPU (Turbo Pascal Unit) that contains the
  505. whole subsystem and may be USEd in your programs.
  506.  
  507.  
  508. The Task Table
  509.  
  510. The central element of the Multi-Tasking Subsystem is called "task
  511. table". For reasons of performance, this table was defined
  512. statically in the data segment. It can hold a maximum of 50 tasks at
  513. the moment, but can easily be expanded (if you own the source code).
  514.  
  515. Every task in the system occupies one entry in the task table. This
  516. entry holds all pieces of information that describe the task's actual
  517. state of execution.
  518.  
  519. At any point of time each task is in one of the following possible
  520. states:
  521.  
  522. Running..: This task is currently executed.
  523.  
  524. Ready....: This task is computable, but another task currently is in
  525.            control of the CPU.
  526.  
  527. Sleeping.: This task has suspended itself for a certain number of
  528.            timer-ticks.
  529.  
  530. Crashed..: The subsystem has detected a stack overflow for this task.
  531.  
  532. Waiting..: This task is currently waiting for an event or a
  533.            resource (a semaphore for example).
  534.  
  535. Sending..: This task is waiting for the receiver of a message to
  536.            do a receive system call.
  537.  
  538. Receiving: This task is waiting for another task to send it a message.
  539.  
  540. Suspended: This task was temporarily disabled by another task in the
  541.            system and needs action from another task to be put into
  542.            the ready-state again.
  543.  
  544. If a slot in the task-table currently is not occupied by any task its
  545. state-marker will contain the value "Available".
  546.  
  547. The following state diagram summarizes all possible task-states and
  548. the state-transitions possible among these states. A state-transition
  549. may only take place in the direction indicated by the arrow symbols.
  550.  
  551.  
  552.  
  553.  
  554.  
  555.  
  556.                        ┌───────────┐            13 ┌───────────┐
  557.                ┌──────>│  Running  ├───────┬──────>│  Crashed  │
  558.                │       └─────┬─────┘       │       └───────────┘
  559.                │             │             │
  560.               2│            1│             │
  561.                │             │             └────────────┐
  562.                │             │                          │
  563.          ┌─────┴─────┐       │     4 ┌───────────┐      │
  564.          │   Ready   │<──────┴─────┬─┤ Waiting   │<─┐3  │
  565.          └───────────┘             │ ├───────────┤  │   │
  566.                                   6├─┤ Sleeping  │<─┤5  │
  567.                                    │ ├───────────┤  │7  │
  568.                                   8├─┤ Sending   │<─┼───┘
  569.                                    │ ├───────────┤  │
  570.                                  10├─┤ Receiving │<─┤9
  571.                                    │ ├───────────┤  │
  572.                                  12└─┤ Suspended │<─┘11
  573.                                      └───────────┘
  574.  
  575.  
  576.  
  577.  
  578.             State Diagram, Multi-Tasking Subsystem Version 1.10
  579.  
  580.  
  581. Task Management
  582.  
  583. Internally, the tasks are managed using queues. Dependent on its
  584. state of execution, a task may be located in
  585.  
  586. a) the scheduler queue of its priority-level ("Running" or "Ready"),
  587. b) the sleep-queue ("Sleeping") or
  588. c) the sender-queue of the receiver-task, if the latter one is not
  589.    ready to receive the message and the sender indicates that it
  590.    wants to wait until the receiver gets ready ("Sending").
  591.  
  592. Tasks at the states "Suspended", "Receiving" or "Crashed" are
  593. not located in any queue.
  594. A task that is "Waiting" on an event or a resource may be chained to
  595. the event-table or be located in a semaphore's task-queue.
  596.  
  597. Whenever a task is created, a task-table entry is allocated to
  598. this task. The slot number of its task-table entry becomes the
  599. task-id (task-number or process-number), which some system-calls
  600. are to be passed as a parameter.
  601. In addition to that, a private stack for the task is allocated and
  602. set up.
  603.  
  604. The task creation can be completed sucessfully only if a) there is
  605. an empty slot in the task-table and b) the private stack can be
  606. allocated on the heap.
  607.  
  608.  
  609.  
  610. Code-Sharing
  611.  
  612. The Turbo Pascal compiler generates object code that is mostly
  613. reentrant. Such code may be executed in code-sharing.
  614.  
  615. The term "Code-sharing" can have different meanings. On the one
  616. hand there may be a number of subroutines which are used by more
  617. than one task in the system. Thus more than one task may be
  618. executing a certain subroutine at the "same" point of time, i. e.
  619. the instruction pointers of those tasks point to a location inside
  620. this subroutine at the same point of time.
  621. Provided the code is reentrant, this will never lead to any
  622. problems. You will have to take care of situations which lead to
  623. more than one task modify global data at the same point of time, of
  624. course, but the type of code-sharing described above will never lead to
  625. a crash or the modification of local variables.
  626.  
  627. "Code-sharing" may also describe a situation, in which the same part
  628. of code (here: the object code of a procedure - the task) is activated
  629. more than once as a separate process. Each of these tasks is given
  630. its own set of registers, its private stack and its local variables.
  631. The latter are actually allocated on the private stack. Provided the
  632. code is reentrant and provided that you, the programmer, take care of
  633. race conditions, there may be an "unlimited" number of separate tasks
  634. executing the same object code simultaneously.
  635.  
  636. I have already tried both kinds of code-sharing and did not encounter
  637. any problems so far.
  638.  
  639.  
  640. Programmable Timers
  641.  
  642. Lets now have a closer look at the timers. A timer, which in fact is
  643. a memory location containing a counter beeing decremented at any
  644. timer-tick, needs to be examined whenever a clock-interrupt occurs.
  645. If the kernel realizes that the timer has expired, some action will
  646. be to be taken.
  647. As you see, a small extra amount of computing power is necessary to
  648. countdown timers, act upon their expiration and finally remove them
  649. from the system. Some types of application, communication programs
  650. for example require extensive use of timers. Therefore one should
  651. think about an inexpensive implementation of timers to avoid loss of
  652. performance.
  653. The implementation of timers in the Multi-Tasking Subsystem makes the
  654. kernel overhead constant and independant from the number of timers
  655. presently active. This is achieved by a special queueing method,
  656. taken from the timer implementation of MINIX, a UNIX-like operating
  657. system for the IBM PC. As stated earlier, MINIX has been developed by
  658. Andrew S. Tanenbaum and is currently distributed by Prentice Hall. I
  659. recommend MINIX for everybody, who would like to go deeper into
  660. operating system design, it's fantastic!
  661.  
  662. Return to the timers: The timer-queue consists of a forward-chained
  663. list of timer-structures. Only the first element of this list is
  664. examined at every timer-tick. If the first timer expires, it will be
  665. removed from the queue after having taken the appropriate action to
  666. signal its expiration to the application. The second element now
  667. becomes the head of the queue, and so on.
  668. When a new timer is inserted into the timer-queue, its counter will be
  669. adjusted to reflect the number of ticks remaining after all previous
  670. elements of the timer-queue have expired.
  671.  
  672. Example:
  673.                 ┌─────┬─┐    ┌─────┬─┐    ┌─────┬─┐
  674. Timer-Queue ───>│  3  │─┼───>│  5  │─┼───>│  1  │─┼─┤
  675.                 └─────┴─┘    └─────┴─┘    └─────┴─┘
  676. Time to wait:      3 Ticks      8 Ticks      9 Ticks
  677.  
  678.  
  679. Event Handling
  680.  
  681. You might think of lots of events that could be handled by an
  682. application.
  683. Currently only the change of contents with respect to a single
  684. memory location is supported. Practically speaking, your application
  685. may wait (without consuming CPU) until the contents of a particular
  686. memory location changes. This memory location is checked whenever a
  687. task-switch occurs.
  688.  
  689. What to do with it?  Well, I came up with this type of event as I
  690. designed a special application. I had to realize a task that emptied
  691. a ring-buffer which was filled by an interrupt routine. Why not use a
  692. semaphore, you might ask!  - Too slow!  There was just enough time to
  693. insert the characters received into the ring-buffer. On the other
  694. hand the ring-buffer had to be emptied as quickly as possible, again
  695. for the reasons of speed.
  696. A high-priorized subtask permanently looping, keeping an eagle eye on
  697. the ring-buffer is far from beeing a solution - it simply blocks the
  698. whole system. OK, lets try to have the subtask sleep for a while,
  699. take a look at the buffer and sleep again if no characters have
  700. arrived. Well, in the meantime a buffer-overflow has occured...
  701.  
  702. I suppose, you now understand why the event has been born. Defining
  703. an event that monitored the buffer's tail-pointer, I kept my
  704. high-priority subtask waiting for the buffer to be filled without
  705. blocking the whole system. Awakend very shortly after a change of the
  706. tail-pointer signalizes incoming characters, it busily processed the
  707. buffer contents and went to sleep again. - Works fine!
  708.  
  709.  
  710. Inter Process Communication (IPC)
  711.  
  712. As mentioned earlier, the Multi-Tasking Subsystem provides for
  713. various methods to exchange data between separate tasks and to
  714. synchronize processes executing simultaneously.
  715. I do not intend to go into detail about the underlying concepts,
  716. because there are lots of books available in the market, which discuss
  717. these topics much better than I could do here in this document. Again
  718. I recommend the study of "Operating Systems Design and Implementation"
  719. written by Andrew S. Tanenbaum.
  720.  
  721.  
  722. A. Message Passing
  723.  
  724. The system-calls supporting message passing are quite
  725. straightforward.
  726. A task that wants to send a message to another task in the system
  727. primarily has to know the task-id of the process it would like to
  728. send to. Then it passes its message-buffer and the receiver-id to
  729. the appropriate system-call. An additional parameter tells the
  730. kernel, whether the sender wants to wait if the receiver is not
  731. ready to receive the message or whether the send-request should be
  732. rejected.
  733.  
  734. A task is ready to receive, whenever it executes a receive-system-
  735. call. It may specify the task-id of the process it wants to receive
  736. from or pass a don't-care code to the system-call, which causes the
  737. kernel to pass through any message sent to this task.
  738. Again, the task may wait until a message arrives or have the system
  739. reject a receive system-call whenever no message is available.
  740.  
  741.  
  742. B. Semaphores
  743.  
  744. There has been written a lot about this topic in the past. Let's have
  745. a look at the internal represetation only at this point.
  746.  
  747. A semaphore as defined by the Multi-Tasking Subsystem, consists of a
  748. two-byte signal count, which is set to 1 at the time of creation,
  749. and a task-queue to which the processes waiting for this semaphore
  750. are appended.
  751. The busy-state is indicated by a signal-count of zero; any non-zero
  752. value indicates a free-state. Whenever a wait system-call is executed
  753. and the corresponding semaphore is currently busy, the requesting
  754. task is suspended until another process releases this semaphore
  755. through execution of a signal system-call.
  756. Whenever a signal system-call is executed and the task-queue of the
  757. corresponding semaphore is not empty, the first task of this queue
  758. is made computable, otherwise the signal-count is incremented.
  759.  
  760. Despite the internal structure, tasks refer to semaphores through
  761. untyped pointers. The task creating a semaphore receives this
  762. pointer on return from the system-call and uses it as a handle
  763. furtheron. The actual number of semaphores in the system is
  764. delimited by the amount of free heap space only.
  765.  
  766.  
  767. Critical Sections
  768.  
  769. You may use the standard functions/procedures supplied with your
  770. compiler freely inside your tasks. BUT - procedures like WriteLn are
  771. not designed to work in multi-tasking environments, that means, they do
  772. not protect their critical sections from beeing entered more than once
  773. at a time.
  774. An example will help to understand what I try to explain: Lets think
  775. of two equaly priorized tasks which spend their lives WriteLn-ing
  776. strings to the terminal screen. The process of bringing a bunch of
  777. characters to the screen is much more complicated than the simple
  778. WriteLn-statement makes believe. Therefore it is likely that
  779. sometimes both tasks will be preempted while in the middle of
  780. performing the screen-write. As a consequence the screen output
  781. becomes clobbered.
  782. Imagine the effects of non-synchronized execution of GetMem and
  783. FreeMem...
  784.  
  785. Another situation in which you have to block a certain section of code
  786. is a routine, that modifies global data or performs some closely
  787. connected actions that form an undivisible unity (atomic action).
  788. Let me give you an example: Provided you got two tasks executing
  789. simultaneously, which at some point of execution output a character to
  790. the screen at a certain position. This is done by executing a GotoXY,
  791. followed by a Write. If the first task was interrupted after having
  792. executed the GotoXY but before having output the character, the second
  793. task might move the cursor to another location on the screen, which
  794. indeed wouldn't lead to the results desired.
  795.  
  796. There are two basically different approaches to solve this problem.
  797. First you could force exclusive CPU-access during the execution of
  798. your critical section. This solution is only applicable for very
  799. small portions of code, a GotoXY/Write-sequence for example, because
  800. a CPU-bind blocks any scheduler function including the sleep-queue
  801. and timer management.
  802. In most cases a protection scheme using semaphores is preferable.
  803. You can find an example for this kind of process synchronisation in
  804. the example program PRO_CON.PAS, routines RBuffGet and RBuffPut. A
  805. section of code protected by a semaphore, may only be entered by one
  806. task at a time. Any task trying to enter the critcal section whilst
  807. another process is currently inside is suspended until the latter one
  808. has left the protected area.
  809.  
  810.  
  811.  
  812. Global Type- and Data-Definitions
  813.  
  814.  
  815. Global Type-Definitions
  816.  
  817. Priority = (Pri_Nice, Pri_User, Pri_Kernel);
  818.  
  819.   This type-definition is used to describe the priority-level at
  820.   which a task is to be executed.
  821.  
  822.  
  823. WaitFlagType = (Wait, NoWait);
  824.  
  825.   Passed as a parameter to the system-calls concerned with
  826.   message-passing, this type indicates whether a process intends to
  827.   wait for a) a message to be received or b) a message available to
  828.   be received.
  829.  
  830.  
  831. TaskReturn = (Task_Crashed, Task_NotFound, Task_NoMsg,
  832.               Task_NotReady, Task_OK, Task_Invalid);
  833.  
  834.   The system-calls concerned with message-passing return one of these
  835.   codes to the calling process.
  836.  
  837.   Task_Crashed...: A task was addressed, which has been disabled
  838.                    following a stack-overflow condition
  839.   Task_NotFound..: The task-table entry indexed by the task-id
  840.                    passed to the executed system-function is
  841.                    out of range or currently empty
  842.   Task_NoMsg.....: There is no message available (Receive with NoWait)
  843.   Task_NotReady..: The receiver is not waiting for a message (Send
  844.                    with NoWait)
  845.   Task_OK........: Fine, that's it
  846.   Task_Invalid...: An invalid task-id was passed to the
  847.                    system-function
  848.  
  849.  
  850. TaskStatus = (Running, Ready, Sleeping, Available, Crashed,
  851.               Waiting, Sending, Receiving, Suspended);
  852.  
  853.   Return value returned by GetTaskState.
  854.  
  855.  
  856. SemReturn = (Sem_NoSpace, Sem_NotFree, Sem_OK, Sem_Invalid);
  857.  
  858.   Some system-calls concerned with semaphore-handling return a value
  859.   of type SemReturn.
  860.  
  861.   Sem_NoSpace..: There is no heap space available
  862.   Sem_NotFree..: You have tried to delete a semaphore whose task-queue
  863.                  is not empty
  864.   Sem_OK.......: Everything's allright
  865.   Sem_Invalid..: You passed an invalid semaphore-handle (NIL)
  866.  
  867.  
  868. TaskNoType = Integer;
  869.  
  870.   Higher level representation of the task-id. I decided to redefine
  871.   the type integer to be able to change this definition in a later
  872.   version without having to think about the program code.
  873.  
  874.  
  875. BPtr = ^Boolean;
  876.  
  877.   Definition required by GetTimBusy.
  878.  
  879.  
  880.  
  881. Global Constants
  882.  
  883. Task_NoSpace
  884.  
  885.   If task creation fails, because there is not enough free dynamic
  886.   memory available to allocate a private stack, CreateTask will return
  887.   this value instead of the task-id.
  888.  
  889.  
  890. Task_NoSlot
  891.  
  892.   If task creation fails, because the maximum number of simultaneously
  893.   active tasks is reached, CreateTask will return this value instead
  894.   of the task-id.
  895.  
  896.  
  897. StateText
  898.  
  899.   StateText is an array indexed by a value of type TaskStatus, which
  900.   contains a textual representation of every possible task state.
  901.  
  902.  
  903. AnyTask
  904.  
  905.   Don't-care-value, passed to the receive system-function whenever a
  906.   process doesn't care from whom it is going to receive a message.
  907.  
  908.  
  909. Global Variables
  910.  
  911. InInt28 (Boolean)
  912.  
  913.   Whenever COMMAND.COM is waiting for input, it permanently issues
  914.   an INT 28H (DOS multi-tasking interrupt). MtPopUp for example
  915.   catches this interrupt and sets InInt28 to TRUE whilst inside the
  916.   interrupt-handler. This informs the kernel know that a task-switch
  917.   may safely performed although the DOS critical-flag is set.
  918.  
  919.  
  920.  
  921. Reference Section (alphabetically sorted)
  922.  
  923.                                                              BindCpu
  924.  
  925.  
  926. Declaration
  927.  
  928.    PROCEDURE BindCPU;
  929.  
  930. Purpose
  931.  
  932.    BindCPU is used to bind the CPU to the currently running process
  933.    temporarily. No task-switch will be performed an no other
  934.    scheduler activities take place until the CPU is released by a
  935.    ReleaseCPU call.
  936.  
  937.  
  938. Example
  939.  
  940.       ...
  941.    BindCPU;                                    {gain CPU exclusivly}
  942.    GotoXY(1,10);                               {atomic action}
  943.    Write('*');
  944.    ReleaseCPU;
  945.       ...
  946.  
  947. Remarks
  948.  
  949.    BindCPU blocks the whole kernel! While the CPU is bound, no
  950.    event-checks, no timer-countdown and no sleep-queue handling are
  951.    performed.
  952.    You should only use BindCPU to protect VERY small portions of
  953.    code. In most cases, the use of semaphores is recommended
  954.  
  955. See Also
  956.  
  957.    ReleaseCPU
  958.  
  959.  
  960.  
  961.                                                CancelTimer / CreateSem
  962.  
  963.  
  964. Declaration
  965.  
  966.    FUNCTION CancelTimer(MemPtr:Pointer):BOOLEAN;
  967.  
  968. Purpose
  969.  
  970.    If there exists a timer that will cause the memory location at the
  971.    address "MemPtr" to become incremented, it will be removed from the
  972.    system. CancelTimer returns TRUE, if a timer could be found,
  973.    otherwise FALSE is returned.
  974.  
  975. Example
  976.  
  977.    VAR Timeout : Boolean;
  978.        Ok      : Boolean;
  979.          ...
  980.    Timeout := False;                           {initialize}
  981.    IF QueueTimer(@Timeout,Seconds(1))          {create Timer}
  982.       THEN  BEGIN
  983.                REPEAT                          {wait}
  984.                ...                             {some action}
  985.                UNTIL Timeout OR Ok;
  986.                IF CancelTimer(@Timeout) THEN;  {Kill Timer}
  987.                ...                             {some more action}
  988.             END;
  989.  
  990. See Also
  991.  
  992.    QueueTimer
  993.  
  994.  
  995. ────────────────────────────────────────────────────────────────────
  996.  
  997.  
  998. Declaration
  999.  
  1000.    FUNCTION CreateSem(VAR SemPtr:Pointer):SemReturn;
  1001.  
  1002. Purpose
  1003.  
  1004.    CreateSem allocates a semaphore on the heap and returns a pointer
  1005.    that functions as a handle in further system-calls referring to
  1006.    this semaphore. The handle is placed into the variable SemPtr.
  1007.    The return value of this system-function indicates whether the
  1008.    action could be completed successfully.
  1009.  
  1010. Example
  1011.  
  1012.    VAR  Semaphore : Pointer;       {Handle}
  1013.          ...
  1014.    IF CreateSem(Semaphore) <> Sem_Ok
  1015.       THEN Error;
  1016.  
  1017. See Also
  1018.  
  1019.    Typedefinition SemReturn, RemoveSem
  1020.                                                           CreateTask
  1021.  
  1022.  
  1023. Declaration
  1024.  
  1025.    FUNCTION CreateTask(TaskPointer:Pointer;Level:Priority;
  1026.             BytesStack:Word):TaskNoType;
  1027.  
  1028. Purpose
  1029.  
  1030.    This function is used to create a subtask.
  1031.    The address of the procedure to be activated as an independently
  1032.    running task is to be passed in "TaskPointer".
  1033.    "Level" describes the desired priority level.
  1034.    Finally, the parameter "BytesStack" contains the desired size of
  1035.    the task's private stack in bytes.
  1036.  
  1037.    CreateTask returns the task-id or a negative value indicating an
  1038.    error-condition.
  1039.  
  1040. Example
  1041.  
  1042.    PROCEDURE SubTask;
  1043.    {
  1044.      I am a task that beeps every second
  1045.    }
  1046.    BEGIN {SubTask}
  1047.       REPEAT                                   {  Body    ─┐ }
  1048.          Sleep(Seconds(1));                    {           │ }
  1049.          Sound(1000);                          { ACTION!!  │ }
  1050.          Delay(20);                            {    :      │ }
  1051.          NoSound;                              {           │ }
  1052.       UNTIL False;                             {  Body    ─┘ }
  1053.    END;  {SubTask}
  1054.          ...
  1055.          ...
  1056.    BEGIN {Main}
  1057.    IF CreateTask(@SubTask,Pri_User,300) < 0
  1058.       THEN Error;
  1059.          ...
  1060.    END. {Main}
  1061.  
  1062. Remarks
  1063.  
  1064.    With respects to programming, a task is realized as a Pascal
  1065.    procedure without parameters, whose body has to be an infinite
  1066.    loop. You must NEVER leave the body of a task except by executing
  1067.    a Terminate system-call.
  1068.    A task is allowed to have local variable definitions, you will
  1069.    have to take care however, that the private stack is properly
  1070.    dimensioned to be able to hold the local data. You will have to
  1071.    add the amount of memory needed to hold the local variables to
  1072.    the bytes stackspace the task needs at runtime.
  1073.    YOU are responsible for passing a valid stack size!!!! This
  1074.    parameter is not checked!!
  1075.  
  1076. See Also
  1077.  
  1078.    Typedefinitions TaskNoType and Priority, Global Constants,
  1079.    Terminate
  1080.  
  1081.  
  1082.                                                              DoShutDown
  1083.  
  1084.  
  1085.  
  1086. Declaration
  1087.  
  1088.    PROCEDURE DoShutDown;
  1089.  
  1090. Purpose
  1091.  
  1092.    Shutdown the Multi-Tasking Subsystem. All interrupt vectors
  1093.    captured are restored to their original state.
  1094.  
  1095. Remarks
  1096.  
  1097.    This procedure is called internally through the exit-procedure of
  1098.    the subsystem-unit.
  1099.  
  1100.                                                        DumpTaskTable
  1101.  
  1102.  
  1103. Declaration
  1104.  
  1105.    PROCEDURE DumpTaskTable;
  1106.  
  1107. Purpose
  1108.  
  1109.    Display the actual state of all active tasks in the system.
  1110.  
  1111. Example
  1112.  
  1113.    Nr. Status      Priorität  Slice  Delay  Stack free  CPU
  1114.      1     Ready      Pri_Nice     2      0         205  18
  1115.      2   Waiting      Pri_User     3      0          -1  1
  1116.      3   Running    Pri_Kernel    -1      0         221  1
  1117.      4   Waiting      Pri_User     2      0         321  5
  1118.  
  1119. Remarks
  1120.  
  1121.    The current content of your display are overwritten. In case you
  1122.    wish to realize a status-window, you should replace DumpTaskTable
  1123.    with a routine of your own.
  1124.  
  1125. See Also
  1126.  
  1127.    Global Constants StateText, GetTaskState
  1128.                                                   EventWait / GetPID
  1129.  
  1130.  
  1131. Declaration
  1132.  
  1133.    FUNCTION EventWait(MemPtr:Pointer):Boolean;
  1134.  
  1135. Purpose
  1136.  
  1137.    EventWait causes your task to be suspended until the contents of
  1138.    the memory-location pointed at by "MemPtr" changes.
  1139.    If no empty event-table entry can be found, EventWait returns
  1140.    FALSE.
  1141.  
  1142. Example
  1143.  
  1144.    VAR HeadPointer : Word;                     {Bufferpointer   }
  1145.        TailPointer : Word;                     {        "       }
  1146.             ...
  1147.    IF NOT EventWait(@TailPointer)              {wait for a change}
  1148.       THEN Error                               {of the pointer}
  1149.       ELSE DoJob;
  1150.             ...
  1151.  
  1152.  
  1153. ────────────────────────────────────────────────────────────────────
  1154.  
  1155.  
  1156. Declaration
  1157.  
  1158.    FUNCTION GetPID:TaskNoType;
  1159.  
  1160. Purpose
  1161.  
  1162.    By calling GetPID, a task can find out its task-id.
  1163.  
  1164. See Also
  1165.  
  1166.    Typedefinition TaskNoType
  1167.                                            GetTaskState / GetTimBusy
  1168.  
  1169.  
  1170. Declaration
  1171.  
  1172.    FUNCTION GetTaskState(Task:TaskNoType):TaskStatus;
  1173.  
  1174. Purpose
  1175.  
  1176.    GetTaskState returns the current status of the task whose task-id
  1177.    was passed to the function.
  1178.  
  1179. Example
  1180.  
  1181.    This task displays its own state on the screen:
  1182.  
  1183.    Writeln(StateText[GetTaskState(GetPID)]);
  1184.  
  1185. See Also
  1186.  
  1187.    Typedefinitions TaskStatus and TaskNoType, Constant StateText
  1188.  
  1189.  
  1190. ────────────────────────────────────────────────────────────────────
  1191.  
  1192.  
  1193. Declaration
  1194.  
  1195.    FUNCTION GetTimBusy:BPtr;
  1196.  
  1197. Purpose
  1198.  
  1199.    GetTimBusy returns the address of the timer-busy flag which
  1200.    contains a non-zero value whenever the interrupt-handling
  1201.    procedure for the INT 8 is active.
  1202.    Sometimes an external interrupt-handler needs to know, whether
  1203.    the scheduler is currently busy.
  1204.    No system-call must be executed whilst the busy-flag is set.
  1205.  
  1206. See Also
  1207.  
  1208.    Typedefinition BPtr
  1209.  
  1210.                                             QueueTimer / ReadySuspended
  1211.  
  1212.  
  1213. Declaration
  1214.  
  1215.    FUNCTION QueueTimer(MemPtr:Pointer; Counter:Word):BOOLEAN;
  1216.  
  1217. Purpose
  1218.  
  1219.    Creates a timer that expires after "Counter" ticks and increments
  1220.    the contents of the memory-location pointed at by "MemPtr".
  1221.    If there is not enough heap space available to allocate a timer,
  1222.    QueueTimer will return FALSE, otherwise TRUE is returned.
  1223.  
  1224. Example
  1225.  
  1226.    VAR Timeout : Boolean;                      {Timeout marker}
  1227.             ...
  1228.    Timeout := False;                           {initialize    }
  1229.    IF NOT QueueTimer(@Timeout,Seconds(1))      {Timeout after 1 sec}
  1230.       THEN Error;
  1231.             ...
  1232.    IF CancelTimer(@Timeout)                    {Remove Timer     }
  1233.       THEN ;                                   {if still existent}
  1234.             ...
  1235.  
  1236.  See Also
  1237.  
  1238.    CancelTimer
  1239.  
  1240.  
  1241. ────────────────────────────────────────────────────────────────────
  1242.  
  1243.  
  1244. Declaration
  1245.  
  1246.    FUNCTION ReadySuspended(Task:TaskNoType):BOOLEAN;
  1247.  
  1248. Purpose
  1249.  
  1250.    ReadySuspended is used to re-enqueue a task into its
  1251.    scheduler-queue wich has been suspended using the Suspend
  1252.    system-function.
  1253.    If the action could be completed successfully, the value TRUE is
  1254.    returned. A return value of FALSE indicates that the task addressed
  1255.    was not currently suspended.
  1256.  
  1257. See Also
  1258.  
  1259.    Suspend
  1260.                                                                 Receive
  1261.  
  1262.  
  1263. Declaration
  1264.  
  1265.    FUNCTION Receive(FromTask:TaskNoType; MsgBuff:Pointer;
  1266.                     WaitFlag:WaitFlagType):TaskReturn;
  1267.  
  1268. Purpose
  1269.  
  1270.    By executing a Receive system-call, a task can receive a message
  1271.    from another task in the system.
  1272.    The first parameter, "FromTask", indicates from which task the
  1273.    caller would like to receive. If "FromTask" contains "AnyTask",
  1274.    any message is passed through, otherwise only messages sent by
  1275.    the process whose id matches "FromTask" are received.
  1276.    "MsgBuff" points to the start of a message buffer into which the
  1277.    received message is copied. The kernel does not check whether the
  1278.    message received fits into the message buffer - it simply
  1279.    copies!
  1280.    If the caller passes a "WaitFlag" of value "Wait", it will be
  1281.    suspended until a message arrives. Otherwise the kernel
  1282.    immediately returns control to the caller if no message is
  1283.    currently available.
  1284.  
  1285. Example
  1286.  
  1287.    VAR Puffer : String;                        {Message buffer}
  1288.                ...
  1289.    IF Receive(AnyTask,@Puffer,Wait) <> Task_Ok
  1290.       THEN Error
  1291.       ELSE Writeln(Puffer);
  1292.                ...
  1293.  
  1294. Remarks
  1295.  
  1296.    Using the don't care task-id "AnyTask", you could realize a task
  1297.    that spends its life waiting for messages from its environment.
  1298.    If you had to monitor some sensors, for example, you could have a
  1299.    separate task for every sensor to be monitored. Whenever some
  1300.    action has to be taken, the corresponding monitor-task sends a
  1301.    message to a central workhorse that performs the necessary
  1302.    adjustments.
  1303.  
  1304. See Also
  1305.  
  1306.    Constant AnyTask, Typedefinition TaskReturn, Send, ReceivedFrom
  1307.                                            ReceivedFrom / ReleaseCPU
  1308.  
  1309.  
  1310. Declaration
  1311.  
  1312.    FUNCTION ReceivedFrom:TaskNoType;
  1313.  
  1314. Purpose
  1315.  
  1316.    By calling ReceivedFrom, a task can find out, who was the sender
  1317.    of the last message received.
  1318.  
  1319. See Also
  1320.  
  1321.    Typedefinition TaskNoType, Constant AnyTask, Receive
  1322.  
  1323.  
  1324. ────────────────────────────────────────────────────────────────────
  1325.  
  1326.  
  1327. Declaration
  1328.  
  1329.    PROCEDURE ReleaseCPU;
  1330.  
  1331. Purpose
  1332.  
  1333.    Provided you have bound the CPU by executing a BindCPU, you will
  1334.    have to reenable scheduling through execution of a ReleaseCPU.
  1335.  
  1336. Example
  1337.  
  1338.       ...
  1339.    BindCPU;                                    {disable scheduler}
  1340.    GotoXY(1,10);                               {atomic action}
  1341.    Write('*');
  1342.    ReleaseCPU;                                 {...set them free...}
  1343.       ...
  1344.  
  1345. See Also
  1346.  
  1347.    BindCPU;
  1348.                                                    RemoveSem / Sched
  1349.  
  1350.  
  1351. Declaration
  1352.  
  1353.    FUNCTION RemoveSem(VAR SemPtr:Pointer):SemReturn;
  1354.  
  1355. Purpose
  1356.  
  1357.    RemoveSem physically removes a semaphore. The formerly occupied
  1358.    heap space is returned to the memory pool.
  1359.    "SemPtr" must be a valid semaphore-handle as returned by the
  1360.    CreateSem system-call.
  1361.    RemoveSem returns a value of type SemReturn that indicates
  1362.    success or failure.
  1363.  
  1364. Example
  1365.  
  1366.    VAR  Semaphore : Pointer;                   {Handle}
  1367.                ...
  1368.    IF CreateSem(Semaphore) <> Sem_Ok
  1369.       THEN Error;
  1370.             ...
  1371.    IF RemoveSem(Semaphore) <> Sem_Ok
  1372.       THEN Error;
  1373.             ...
  1374.  
  1375. Remarks
  1376.  
  1377.    The system mostly is not able to prove whether a valid handle is
  1378.    supplied. It is your job to take care of this.
  1379.    You cannot remove a semaphore, whose task-queue currently is not
  1380.    empty.
  1381.  
  1382. See Also
  1383.  
  1384.    Typedefinition SemReturn, CreateSem
  1385.  
  1386.  
  1387. ────────────────────────────────────────────────────────────────────
  1388.  
  1389.  
  1390. Declaration
  1391.  
  1392.    PROCEDURE Sched;
  1393.  
  1394. Purpose
  1395.  
  1396.    Initiate a task-switch, give up the current timeslice.
  1397.    "Sched" is used internally by many system-functions. If you want
  1398.    to have your task give up its timeslice, you should better use
  1399.    Sleep(1).
  1400.  
  1401.                                                   Seconds / SemClear
  1402.  
  1403.  
  1404. Declaration
  1405.  
  1406.    FUNCTION Seconds(Sec:Word):LongInt;
  1407.  
  1408. Purpose
  1409.  
  1410.    "Seconds" returns the number of timer-ticks that make up a second.
  1411.    It is recommended that you use this function whenever you need to
  1412.    specify a special amount of time. Use of Seconds makes your
  1413.    application independent of changes to the task-switch rate.
  1414.  
  1415. Example
  1416.  
  1417.    IF QueueTimer(@Timeout,Seconds(1) SHR 1)    { timout after   }
  1418.       THEN;                                    { half a second  }
  1419.  
  1420. See Also
  1421.  
  1422.    QueueTimer, CancelTimer
  1423.  
  1424.  
  1425. ────────────────────────────────────────────────────────────────────
  1426.  
  1427.  
  1428.  
  1429. Declaration
  1430.  
  1431.    PROCEDURE SemClear(SemPtr:Pointer);
  1432.  
  1433. Purpose
  1434.  
  1435.    Reset a semaphore's signal-count to zero.
  1436.  
  1437. Example
  1438.  
  1439.    VAR  Semaphore : Pointer;                   {Handle}
  1440.                ...
  1441.    IF CreateSem(Semaphore) <> Sem_Ok
  1442.       THEN Error
  1443.       ELSE SemClear(Semaphore);
  1444.  
  1445. Remarks
  1446.  
  1447.    Internally SemClear is translated into SemSet(Semaphore,0).
  1448.  
  1449. See Also
  1450.  
  1451.    SemSignal, SemWait, SemClearWait, SemSet
  1452.                                                         SemClearWait
  1453.  
  1454.  
  1455. Declaration
  1456.  
  1457.    PROCEDURE SemClearWait(SemPtr:Pointer);
  1458.  
  1459. Purpose
  1460.  
  1461.    Reset a semaphore's signal-count to zero and wait until another
  1462.    task initiates a SemSignal system-call for this semaphore.
  1463.    In contrast to SemWait, SemClearWait ALWAYS leads to the calling
  1464.    task beeing suspended.
  1465.  
  1466. Example
  1467.  
  1468.    VAR Ready : Pointer;                        {Semaphore      }
  1469.          ...
  1470.    PROCEDURE SubTask;
  1471.    {
  1472.       I am a task that needs to save the values of some global
  1473.       variables during initialisation. The main program, however,
  1474.       changes these variables during the process of its execution.
  1475.       Therefore, the main program has to wait until I indicate that I
  1476.       have transferred the values I need to my local data area.
  1477.    }
  1478.    BEGIN {SubTask}
  1479.          ...                                   {initialize     }
  1480.       SemSignal(Ready);                        {O.K. I'm ready }
  1481.       REPEAT
  1482.          ...                                   {Body           }
  1483.       UNTIL False;
  1484.    END;  {SubTask}
  1485.          ...
  1486.          ...
  1487.    BEGIN {Main}
  1488.          ...
  1489.    IF CreateSem(Ready) <> Sem_Ok               {Create a semaphore}
  1490.       THEN Error;
  1491.    IF CreateTask(@SubTask,Pri_User,300) < 0    {Create a task     }
  1492.       THEN Error
  1493.       ELSE SemClearWait(Ready);                {wait for O.K.    }
  1494.          ...
  1495.    END. {Main}
  1496.  
  1497. See Also
  1498.  
  1499.    SemClear, SemSet, SemSignal, SemWait
  1500.  
  1501.                                               SemCut / SemGetSignals
  1502.  
  1503.  
  1504. Declaration
  1505.  
  1506.    FUNCTION SemCut(SemPtr:Pointer;Task:TaskNoType):BOOLEAN;
  1507.  
  1508. Purpose
  1509.  
  1510.    If the task whose id is passed as "Task" currently is waiting in
  1511.    "SemPtr"'s task-queue, it is removed from this queue.
  1512.    SemCut returns TRUE, if the action could be completed successfully.
  1513.  
  1514. Remarks
  1515.  
  1516.    SemCut is a somewhat brutal function! It forces an innatural
  1517.    action and was added for a special application purpose in MtPopUp.
  1518.    PLEASE do use it with care!! - Better keep away from it.
  1519.  
  1520. See Also
  1521.  
  1522.    SemPaste
  1523.  
  1524.  
  1525. ────────────────────────────────────────────────────────────────────
  1526.  
  1527.  
  1528. Declaration
  1529.  
  1530.    FUNCTION SemGetSignals(SemPtr:Pointer):Word;
  1531.  
  1532. Purpose
  1533.  
  1534.    Returns the signal-count of the semaphore "SemPtr".
  1535.  
  1536. Example
  1537.  
  1538.    FUNCTION SemBusy(S:Pointer):BOOLEAN;
  1539.    {
  1540.       This function checks whether a semaphore is currently busy.
  1541.    }
  1542.    BEGIN {SemBusy}
  1543.       SemBusy := (SemGetSignals(S) = 0);       {Signal-Count = 0}
  1544.    END;  {SemBusy}
  1545.  
  1546. See Also
  1547.  
  1548.    SemSignal, SemWait, SemClear, SemClearWait, SemSet
  1549.                                                    SemPaste / SemSet
  1550.  
  1551.  
  1552. Declaration
  1553.  
  1554.    PROCEDURE SemPaste(SemPtr:Pointer; Task:TaskNoType);
  1555.  
  1556. Purpose
  1557.  
  1558.    SemPaste unconditionally appends "Task" to the task-queue of the
  1559.    semaphore referred to by "SemPtr". No validity checks are performed!
  1560.  
  1561. Remarks
  1562.  
  1563.  
  1564.    SemCut is a somewhat brutal function! It forces an innatural
  1565.    action and was added for a special application purpose in MtPopUp.
  1566.    PLEASE do use it with care!! - Better keep away from it.
  1567.  
  1568.  
  1569. ────────────────────────────────────────────────────────────────────
  1570.  
  1571.  
  1572. Declaration
  1573.  
  1574.    PROCEDURE SemSet(SemPtr:Pointer; Count:Word);
  1575.  
  1576. Purpose
  1577.  
  1578.    SemSet sets the signal-count of a semaphore to a definite value.
  1579.    This action does not have any influence on tasks that might be
  1580.    waiting in the task-queue belonging to this semaphore.
  1581.  
  1582. Example
  1583.  
  1584.    CONST Elements = 100;
  1585.    VAR   Buffer   : ARRAY[1..Elements] OF Byte; {Buffer}
  1586.          Full     : Pointer;                   {No. of used slots}
  1587.          Empty    : Pointer;                   {No. of empty slots}
  1588.          ...
  1589.    BEGIN {Main}
  1590.          ...
  1591.       IF CreateSem(Full) = Sem_Ok
  1592.          THEN SemClear(Full);                  {no slot used}
  1593.       IF CreateSem(Empty) = Sem_Ok
  1594.          THEN SemSet(Empty,Elements);          {all slots free}
  1595.          ...
  1596.    END. {Main}
  1597.  
  1598. See Also
  1599.  
  1600.    SemSignal, SemWait, SemClear, SemClearWait
  1601.                                             SemSignal / SemSoWaiting
  1602.  
  1603.  
  1604. Declaration
  1605.  
  1606.    PROCEDURE SemSignal(SemPtr:Pointer);
  1607.  
  1608. Purpose
  1609.  
  1610.    If the task-queue of the semaphore referred to by "SemPtr" is
  1611.    currently empty, the signal-count will be incremented. Otherwise
  1612.    the first task waiting is re-scheduled and the signal-count remains
  1613.    unchanged.
  1614.  
  1615. Example
  1616.  
  1617.    VAR Critical: Pointer;
  1618.          ...
  1619.    IF SemCreate(Critical) <> Sem_Ok
  1620.    THEN Error;
  1621.          ...
  1622.    SemWait(Critical);
  1623.          ...
  1624.    {critical section}
  1625.          ...
  1626.    SemSignal(Critical);
  1627.          ...
  1628.  
  1629. See Also
  1630.  
  1631.    SemWait, SemClearWait, SemClear, SemSet
  1632.  
  1633.  
  1634. ────────────────────────────────────────────────────────────────────
  1635.  
  1636.  
  1637. Declaration
  1638.  
  1639.    FUNCTION SemSoWaiting(SemPtr:Pointer):BOOLEAN;
  1640.  
  1641. Purpose
  1642.  
  1643.    SemSoWaiting returns TRUE, if there are tasks waiting for
  1644.    "SemPtr" to become available.
  1645.  
  1646. See Also
  1647.  
  1648.    SemWait, SemSignal, SemClear, SemClearWait, SemSet, SemGetSignals
  1649.                                                       SemWait / Send
  1650.  
  1651.  
  1652. Declaration
  1653.  
  1654.    PROCEDURE SemWait(SemPtr:Pointer);
  1655.  
  1656. Purpose
  1657.  
  1658.    If a semaphore's signal count currently is greater than zero, it
  1659.    will be decremented. Otherwise the caller is suspended and
  1660.    appended to the task-queue of this semaphore.
  1661.    It remains suspended until another task issues a SemSignal for
  1662.    the semaphore it is waiting for.
  1663.  
  1664. Example
  1665.  
  1666.    VAR Critical: Pointer;
  1667.          ...
  1668.    IF SemCreate(Critical) <> Sem_Ok
  1669.    THEN Error;
  1670.          ...
  1671.    SemWait(Critical);
  1672.          ...
  1673.    {critical section}
  1674.          ...
  1675.    SemSignal(Critical);
  1676.          ...
  1677.  
  1678. See Also
  1679.  
  1680.    SemSignal, SemClearWait, SemClear, SemSet
  1681.  
  1682.  
  1683. ────────────────────────────────────────────────────────────────────
  1684.  
  1685.  
  1686. Declaration
  1687.  
  1688.    FUNCTION Send(ToTask:TaskNoType; Msg:Pointer; MsgSize:WORD;
  1689.                  WaitFlag:WaitFlagType):TaskReturn;
  1690.  
  1691. Purpose
  1692.  
  1693.    The system-function "Send" lets a task send a message to another
  1694.    process in the system.
  1695.    The parameter "ToTask" contains the task-id of the receiver. This
  1696.    must be the task-id of a currently active task. You must not use
  1697.    the constant "AnyTask" in this context.
  1698.    "Msg" points to the start of the message-buffer whose size is
  1699.    passed in "MsgSize".
  1700.    Finally a wait-flag has to be supplied, that indicates whether
  1701.    the sender wishes to wait until the receiver is willing to
  1702.    receive the message. If you set "WaitFlag" to "NoWait", the
  1703.    kernel will return control to your task immedeately if the
  1704.    receiver is not waiting for a message.
  1705.                                                         Send / Sleep
  1706.  
  1707.  
  1708. Example
  1709.  
  1710.    VAR Puffer : String;
  1711.          ...
  1712.    IF Receive(AnyTask,@Puffer,Wait) <> Task_Ok
  1713.       THEN Error
  1714.       ELSE Writeln(Puffer);
  1715.          ...
  1716.  
  1717. Remarks
  1718.  
  1719.    The message is physically copied to the message buffer of the
  1720.    receiving process. This is not the best solution from the point
  1721.    of view of performance, but is provided for the highest possible
  1722.    independence of sender and receiver. They could theoretically be
  1723.    located on different machines in a networking environment. If
  1724.    you better like a pointer to a message to be passed, make your
  1725.    pointer the message.
  1726.  
  1727. See Also
  1728.  
  1729.    Typedefinition TaskReturn, Receive
  1730.  
  1731.  
  1732. ────────────────────────────────────────────────────────────────────
  1733.  
  1734.  
  1735. Declaration
  1736.  
  1737.    PROCEDURE Sleep(Ticks:LongInt);
  1738.  
  1739. Purpose
  1740.  
  1741.    "Sleep" lets your task suspend itself for a certain number of
  1742.    timer-ticks.
  1743.  
  1744. Example
  1745.  
  1746.    Writeln('I''m going to sleep for 3 seconds!');
  1747.    Sleep(Seconds(3));
  1748.    Writeln('Hi, here I am again!');
  1749.  
  1750. See Also
  1751.  
  1752.    Seconds
  1753.                                                 Suspend / Terminate
  1754.  
  1755.  
  1756. Declaration
  1757.  
  1758.    FUNCTION Suspend(Task:TaskNoType):BOOLEAN;
  1759.  
  1760. Purpose
  1761.  
  1762.    Brutally suspend a task which is currently computable.
  1763.    "Task" contains the id of the task to be suspended.
  1764.    A task that has been suspended by a Suspend system-call can ONLY
  1765.    be reactivated by a ReadySuspended system-call.
  1766.    If the task addressed was not computable, Suspend would return
  1767.    FALSE to the caller. Otherwise TRUE is returned.
  1768.  
  1769. See Also
  1770.  
  1771.    Typedefinition TaskNoType, ReadySuspended
  1772.  
  1773.  
  1774. ────────────────────────────────────────────────────────────────────
  1775.  
  1776.  
  1777. Declaration
  1778.  
  1779.    PROCEDURE Terminate;
  1780.  
  1781. Purpose
  1782.  
  1783.    A task may terminate itself by executing a "Terminate"-system-call.
  1784.    The task-descriptor is marked available and the private stack is
  1785.    returned to the memory pool.
  1786.  
  1787. See Also
  1788.  
  1789.    CreateTask
  1790.  
  1791.                                                            TimeSlice
  1792.  
  1793.  
  1794. Declaration
  1795.  
  1796.    PROCEDURE TimeSlice(Slice, DynQ:Byte);
  1797.  
  1798. Purpose
  1799.  
  1800.    The width of a timeslice and the +/- maximum number of ticks to
  1801.    credit/debit during dynamic scheduling, may be changed by issuing
  1802.    a "TimeSlice"-system-call.
  1803.    "Slice" describes the size of a timeslice in timer-ticks;
  1804.    "DynQ" contains the number of ticks to credit/debit during
  1805.    dynamic scheduling.
  1806.    "Slice" defaults to 2; "DynQ" defaults to 10.
  1807.  
  1808.